package com.yu.loader;

import com.yu.annotation.SPI;
import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * @PackageName: com.yu.loader
 * @Description:
 * @Version: v1.0.0
 * @Author: yuweilin
 * @Date: 15:42
 * <p>
 * Modification History:
 * Date         Author          Version            Description
 * ------------------------------------------------------------
 * 2021/1/23      yuweilin         v1.0.0               新建
 */
@Slf4j
public class ExtensionLoader<T> {
    //服务地址目录
    private static final String SERVICE_DIRECTORY = "META-IFN/extensions/";
    //存放着对应的类加载器和对应的类对象
    private static final Map<Class<?>,ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>();
    //存放着类对象和对应的实例
    private static final Map<Class<?>,Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>();
    //loader加载的类对象？
    private final Class<?> type;
    //缓存实例的map？？
    private final Map<String,Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
    //缓存类对象到holder中
    private final Holder<Map<String,Class<?>>> cachedClasses = new Holder<>();

    public ExtensionLoader(Class<?> type) {
        this.type = type;
    }

    /**
     * 不限定类型，但是希望返回的类型是该类型的类加载器
     * @param type
     * @param <S>
     * @return
     */
    public static <S> ExtensionLoader<S> getExtensionLoader(Class<S> type){
        if (type == null){
            throw new IllegalArgumentException("拓展的类型不能为空");
        }
        if (!type.isInterface()){
            throw new IllegalArgumentException("type 的类型必须是一个接口");
        }
        if (type.getAnnotation(SPI.class)==null){
            throw new IllegalArgumentException("type 必须被@SPI注解");
        }
        ExtensionLoader<S> extensionLoader = (ExtensionLoader<S>) EXTENSION_LOADERS.get(type);
        if (extensionLoader==null){
            //如果没有将
            EXTENSION_LOADERS.putIfAbsent(type,new ExtensionLoader<S>(type));
            extensionLoader = (ExtensionLoader<S>)EXTENSION_LOADERS.get(type);
        }
        return extensionLoader;
    }

    /**
     * 根据名字来获取实例，如果没有那么就创建一个存进去
     * holder中没有值，那么创建一个进行存储
     * @param name
     * @return
     */
    public T getExtension(String name){
        if (name==null||name.isEmpty()){
            throw new IllegalArgumentException("拓展名不该是空");
        }
        Holder<Object> holder = cachedInstances.get(name);
        if (holder==null){
            cachedInstances.putIfAbsent(name,new Holder<>());
            holder = cachedInstances.get(name);
        }
        Object instance = holder.get();
        if (instance==null){
            synchronized (holder){
                instance = holder.get();
                if (instance==null){
                    instance = createExtension(name);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }

    private T createExtension(String name){
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz==null){
            throw new RuntimeException("没有此拓展名");
        }
        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        if (instance==null){
            try {
                EXTENSION_INSTANCES.putIfAbsent(clazz,clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }catch (Exception e){
                log.error(e.getMessage());
            }
        }
        return instance;
    }

    private Map<String,Class<?>> getExtensionClasses(){
        Map<String,Class<?>> classes = cachedClasses.get();
        if (classes==null){
            synchronized (cachedClasses){
                classes = cachedClasses.get();
                if (classes == null){
                    classes = new HashMap<>();
                    loadDirectory(classes);
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }
    /**
     * 通过传入一个map来保存目标路径下的url的名字和class
     * @param extensionClasses
     */
    private void loadDirectory(Map<String,Class<?>> extensionClasses){
        String fileName = ExtensionLoader.SERVICE_DIRECTORY + type.getName();
        try {
            Enumeration<URL> urls;
            ClassLoader classLoader = ExtensionLoader.class.getClassLoader();
            urls = classLoader.getResources(fileName);
            if (urls!=null){
                while (urls.hasMoreElements()) {
                    URL resourceUrl = urls.nextElement();
                    loadResource(extensionClasses, classLoader, resourceUrl);
                }
            }
        }catch (IOException e){
            log.error(e.getMessage());
        }
    }
    /**
     * 拿到url地址，然后读取内容，
     * 将读取到的名字和类路径分开，然后通过类加载器加载进去，得到目标class
     * 然后将名字和class存入到map中
     * @param extensionClasses
     * @param classLoader
     * @param resourceUrl
     */
    private void loadResource(Map<String,Class<?>> extensionClasses, ClassLoader classLoader, URL resourceUrl){
        try(BufferedReader reader = new BufferedReader(new InputStreamReader(resourceUrl.openStream(),UTF_8))) {
            String line;
            while ((line=reader.readLine())!=null){
                line = line.trim();
                if (line.length()>0){
                    try {
                        int i = line.indexOf('=');
                        String name = line.substring(0,i).trim();
                        String className = line.substring(i+1).trim();
                        if (name.length()>0&&className.length()>0){
                            Class<?> clazz = classLoader.loadClass(className);
                            extensionClasses.put(name,clazz);
                        }
                    }catch (ClassNotFoundException e){
                        log.error(e.getMessage());
                    }
                }
            }
        }catch (IOException e){
            log.error(e.getMessage());
        }
    }
}
